package exportSystem.statistics;

import gameEngine.agents.observationSystem.samples.Sample;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

import main.Main;

import org.apache.log4j.Logger;

import ui.flowManager.expirimentProperties.ExperimentProperties;


public class ExperimentLog implements GameLog {

	private static ExperimentLog experimentLog;
	
	private int turns;
	private int returns;
	private GameData data;
	private int[] studies;
	private int[] control;
	private int[] wins;
	private Logger log = Logger.getLogger(this.getClass());
	/**
	 * creates the singleton
	 * @return instance of the log 
	 */
	public static synchronized ExperimentLog getInstance(){
		if(experimentLog==null){
			experimentLog=new ExperimentLog();
		}
		return experimentLog;
	}
	
	private ExperimentLog(){
		
	}
	
	/**
	 * Initialize the logger 
	 */
	public void initExperimentLog(int turns,int returns,int[] stud,int[] con){
		this.turns=turns;
		this.returns=returns;
		data=new GameData();
		this.studies=stud;
		this.control=con;
		wins=new int[stud.length];
	}
	
	public void addTurnData(int gameNum,Sample s){
		data.addTurnData(gameNum, s);		
	}
	
	public void addWin(int gameId){
		wins[gameId]++;
	}
	
	public void write(){
		//for each game write the log
		ArrayList<Integer> games=new ArrayList<Integer>();
		games.addAll(data.getGamesIDs());
		File file;
		String str=new String();
		FileWriter fstream=null;
		BufferedWriter out;
		String dirName=System.getProperty(Main.EXPORT_DIR)+"Result\\"+ExperimentProperties.getExperimentName();
		if(!new File(dirName).exists()){
			boolean success = new File (dirName).mkdir();
			if(success){
				log.debug("created directory "+dirName);
				file=new File(dirName,"Experiment_log.csv");	
			}else{
				log.debug("directory not created writing to relative path");
				file=new File("Experiment_log.csv");	
			}
		}else{	
			log.debug(dirName+" exists write to it");
			file=new File(dirName,"Experiment_log.csv");	
		}
		try {
			fstream = new FileWriter(file);
		} catch (IOException e) {
			log.debug("failed to init file stream\n"+ e.getMessage());
		}
		out = new BufferedWriter(fstream);
		try {
			out.write(writeHeader(games.size(),control.length+1));
		} catch (IOException e1) {
			log.error("failed to write header");
		}
		for(int i=0;i<turns;i++){
			str=(i+1)+"";
			for(int j=0;j<games.size();j++){
				TurnLog turnLog=data.getTurnData(games.get(j),i);
				if(turnLog!=null){
				str+=","+turnLog.getAvaregeScore(returns);
				//add each agents avarage score
				str+=","+turnLog.getAgentAvaregeScore(studies[j]);
				for(int id=0;id<control.length;id++)
					str+=","+turnLog.getAgentAvaregeScore(control[id]);
				//add each agents turns palyed
				str+=","+turnLog.getAgentTimesPlayed(studies[j]);
				for(int id=0;id<control.length;id++)
					str+=","+turnLog.getAgentTimesPlayed(control[id]);			
				//turn was not played even once since agent stoped before 
				}else{
					str+=",,";
					for(int id=0;id<control.length;id++)
						str+=",";
					//add each agents turns palyed
					str+=",";
					for(int id=0;id<control.length;id++)
						str+=",";
				}
			}
			str+="\n";
			try {
				out.write(str);
			} catch (IOException e) {
				log.error("failed to write");
			}
		}
		str="\n\n\n";
		for(int j=0;j<games.size();j++){
			double turn=Statistics.getAgentStopAvarage(studies[j], returns);
			str+="agent "+studies[j]+" stopes the game at average turn: "+turn+"\n";
		}
		str+=wins[0]+"";
		for(int i=1;i<wins.length;i++)
			str+=","+wins[i];
		try {
			out.write(str);
		} catch (IOException e) {
			log.error("failed to write");
		}
		try {
			out.close();
		} catch (IOException e) {
			log.error("failed to write");
		}
		
	}
	/**
	 * create a header
	 * @param gameNum number of games to play
	 * @param agentNum number of agents at each game
	 * @return
	 */
	private String writeHeader(int gameNum,int agentNum){
		String str=",";
		for(int i=1;i<=gameNum;i++){
			str+="game "+i;
			for(int j=0;j<=agentNum*2;j++)
				str+=",";
		}
		str+="\n";
		str+="turn";
		for(int i=1;i<=gameNum;i++){
			str+=",AVG turn score";
			for(int j=1;j<=agentNum;j++)
				str+=",agent "+j+"AVG Score";
			for(int j=1;j<=agentNum;j++)
				str+=",agent "+j+"NUM time played";
		}
		return str+"\n";
	}
	/**
	 * this class holds the games data per turn
	 * @author berkoviz
	 *
	 */
	private class GameData{
		HashMap<Integer,HashMap<Integer,TurnLogImpl>> data=new HashMap<Integer,HashMap<Integer,TurnLogImpl>>();
		/**
		 * add turn log data
		 * @param gameNum game number
		 * @param s sample created by turn
		 */
		public void addTurnData(int gameNum,Sample s){
			//if not created add it
			if(data.get(gameNum)==null){
				HashMap<Integer,TurnLogImpl> gameTurnRow=new HashMap<Integer,TurnLogImpl>();	
				data.put(gameNum, gameTurnRow);
			}else{
				//empty round create it 
				if(data.get(gameNum).get(s.getRound())==null){
					TurnLogImpl turnLog=new TurnLogImpl();
					turnLog.readSample(s);
					data.get(gameNum).put(s.getRound(),turnLog);
				}else{
					data.get(gameNum).get(s.getRound()).readSample(s);
				}
			}
			
		}
		/**
		 * return turn log by game number and turn number
		 * @param gameNum game index 
		 * @param turnNum turn index
		 * @return
		 */
		public TurnLog getTurnData(int gameNum,int turnNum){
			//if not created 
			if(data.get(gameNum)==null){
				return null;
			}else{
				//empty round 
				if(data.get(gameNum).get(turnNum)==null){
					return null;
				}else{
					return data.get(gameNum).get(turnNum);
				}
			}
			
		}
		/**
		 * get indexes of games
		 * @return
		 */
		public Set<Integer> getGamesIDs(){
			return data.keySet();
		}
	}
	
}
